package dataAccess;

import au.com.bytecode.opencsv.CSVReader;
import businessEntities.MainLogger;
import businessEntities.NodeData;
import businessEntities.QueryResultContainer;
import businessEntities.TreeModelWrapper;
import businessEntities.exceptions.CorruptFileException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class DALFileImporter {

    /**
     * Reads data from <code>inputFile</code> formatted as CSV.
     * @return QueryResultContainer with the result or null if any problem occurred.
     */
    public QueryResultContainer importFromCSV(File inputFile) {

        QueryResultContainer result = null;
        MainLogger.logger.info("Start importing data from CSV file " + inputFile);
        try (CSVReader reader = new CSVReader(new FileReader(inputFile))) {
            List<String> headers = new ArrayList<>(Arrays.asList(reader.readNext()));
            List<String[]> data = new ArrayList<>();
            String[] next;
            while ((next = reader.readNext()) != null) {
                if (next.length != headers.size()) {
                    throw new CorruptFileException("Corrupt input file. The file cannot be imported.");
                }
                data.add(next);
            }
            result = new QueryResultContainer(headers, data);
        } catch (IOException ex) {
            MainLogger.logger.error("Error while trying to import data from CSV file.", ex);

        } catch (NullPointerException nullEx) {
            MainLogger.logger.error("Error while trying to import data from CSV file. Can't import null file.", nullEx);

        } catch (CorruptFileException corruptEx) {
            MainLogger.logger.error("Error while trying to import data from CSV file. Corrupt input file.", corruptEx);
            result = null;
        }

        return result;
    }

    /**
     * Reads data from <code>inputFile</code> formatted as XML.
     * @return QueryResultContainer with the result or null if any problem occurred.
     **/
    public QueryResultContainer importFromXML(File inputFile) {

        QueryResultContainer result = null;
        MainLogger.logger.info("Start importing data from XML file " + inputFile);
        try {

            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(inputFile);

            doc.getDocumentElement().normalize();
            NodeList root = doc.getDocumentElement().getChildNodes();

            //MetaData
            List<String> headers = new ArrayList<>();
            Node metaData = root.item(1);
            NodeList columnsList = metaData.getFirstChild().getChildNodes();
            for (int i = 0; i < columnsList.getLength(); i++) {
                String header = columnsList.item(i).getFirstChild().getFirstChild().getNodeValue();
                headers.add(header);
            }

            //Data
            List<String[]> rowsData = new ArrayList<>();
            Node data = root.item(2);
            NodeList rowsList = data.getFirstChild().getChildNodes();
            for (int row = 0; row < rowsList.getLength(); row++) {
                String[] currentRow = new String[columnsList.getLength()];
                NodeList rowContent = rowsList.item(row).getChildNodes();
                for (int col = 0; col < columnsList.getLength(); col++) {
                    Node cellContent = rowContent.item(col).getFirstChild();
                    if (cellContent == null) {
                        currentRow[col] = null;
                    } else {
                        currentRow[col] = cellContent.getNodeValue();
                    }
                }
                rowsData.add(currentRow);
            }

            result = new QueryResultContainer(headers, rowsData);
        } catch (ParserConfigurationException | SAXException | NullPointerException | IOException ex) {
            MainLogger.logger.error("Error while trying to import data from XML file.", ex);
            result = null;
        }

        return result;
    }
    
    /**
     * Reads tree from <code>inputFile</code> formatted as XML.
     * @return TreeModelWrapper with the result or null if any problem occurred.
     **/
    public TreeModelWrapper importTreeFromXML (File inputFile){
        TreeModelWrapper result = null;
        MainLogger.logger.info("Start import tree form XML file : " + inputFile);
        try {
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(inputFile);
            
            doc.getDocumentElement().normalize();
            NodeList root = doc.getDocumentElement().getChildNodes().item(0).getChildNodes();
            
            String database = root.item(0).getFirstChild().getNodeValue();
            String table = root.item(1).getFirstChild().getNodeValue();
            
            NodeData rootNodeData = NodeData.createRootNodeData(database, table);
            DefaultMutableTreeNode rootTreeNode = new DefaultMutableTreeNode(rootNodeData);
            
            for(int i = 2 ; i < root.getLength() ; i++){
                Node child = root.item(i);
                addChild(child, rootNodeData, rootTreeNode);
            }
            
            DefaultTreeModel treeModel = new DefaultTreeModel(rootTreeNode);
            result = new TreeModelWrapper(treeModel, database, table);
        } catch (ParserConfigurationException | SAXException | IOException | NullPointerException ex) {
            MainLogger.logger.error("Could not import tree form XML file : " + inputFile,ex);
            result = null;
        }
        
        return result;
    }
    
    public void addChild(Node child,NodeData parentData,DefaultMutableTreeNode parentTreeNode){
        String filter = child.getFirstChild().getFirstChild().getNodeValue();
        NodeData childData = new NodeData(filter, parentData);
        DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(childData);
        parentTreeNode.add(childTreeNode);
        
        NodeList childList = child.getChildNodes();
        for(int i = 1; i < childList.getLength() ; i++){
            Node childOfChild = childList.item(i);
            addChild(childOfChild, childData, childTreeNode);
        }   
    }
    
    
    
}
